//------------------------------------------------------------------------------
//	file midi_sysex.c
//	brief
//		MIDI tools
//		Read / Write MIDI systex messages
//
//	$Id: midi_sysex.c 16 2015-03-25 05:53:57Z ac.verbeck@gmail.com $
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//	Copyright and Disclaimer Notice
//
//	The MIT License (MIT)
//	
//	Copyright (c) 2014 A.C. Verbeck
//	
//	Permission is hereby granted, free of charge, to any person obtaining a copy
//	of this software and associated documentation files (the "Software"), to deal
//	in the Software without restriction, including without limitation the rights
//	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//	copies of the Software, and to permit persons to whom the Software is
//	furnished to do so, subject to the following conditions:
//	
//	The above copyright notice and this permission notice shall be included in
//	all copies or substantial portions of the Software.
//	
//	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//	THE SOFTWARE.
//
//------------------------------------------------------------------------------

#include <stdio.h>
#include <conio.h>
#include <stdlib.h>

#include <stdint.h>																//	Standard int types
#include <stdbool.h>															//	Bool

#include <popt.h>
#include <Windows.h>
#include <mmsystem.h>

#include "bi.h"
#include "debug.h"
#include "p_mem.h"

//----------------------------------------------------------------------------
// Local defines (options)
//----------------------------------------------------------------------------
#define OPT_INPUT			0x0001
#define OPT_OUTPUT			0x0002
#define OPT_ABOUT			0x0100
#define OPT_VERBOSE			0x0200

//----------------------------------------------------------------------------
// Local defines (sysex buffers)
//	Used for both input and output buffers
//----------------------------------------------------------------------------
#define SYSEX_BUFF_CT		4
#define SYSEX_BUFF_SZ		256

//----------------------------------------------------------------------------
// Local data
//----------------------------------------------------------------------------
static poptContext		optCon;

//----------------------------------------------------------------------------
// Local functions
//----------------------------------------------------------------------------
static void CALLBACK MIDI_proc_in(HMIDIIN h, UINT wMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2);
static void CALLBACK MIDI_proc_out(HMIDIIN h, UINT wMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2);
static void about(void);

//------------------------------------------------------------------------------
//	Main application
//	- read parameters
//	- start the midi port
//	- wait for characters
//------------------------------------------------------------------------------
int main(int argc, char* argv[])
{
	HMIDIIN		hMIDI_in = NULL;
	HMIDIOUT	hMIDI_out = NULL;
	MIDIHDR		SysExHdr[SYSEX_BUFF_CT];

	DWORD		MIDI_in = 0;
	DWORD		MIDI_out = 0;
	MMRESULT	rVal;

	uint32_t	opt = 0;
	int			idx = 0;
	int			in_char = 0;
	bool		tr_run = true;

	struct poptOption optionsTable[] = {
		{ "input",	'i', POPT_ARG_INT,	&MIDI_in,	'i', "MIDI input port to use",				NULL	},
		{ "output",	'o', POPT_ARG_INT,	&MIDI_out,	'o', "MIDI output port to use",				NULL	},
		{ "about",	'a', POPT_ARG_NONE,	NULL,		'a', "display information about the app",	NULL	},
		{ "verbose",'v', POPT_ARG_NONE,	NULL,		'v', "enable verbose mode",					NULL	},
		POPT_AUTOHELP
		POPT_TABLEEND
	};

//------------------------------------------------------------------------------
//	Process arguments
//------------------------------------------------------------------------------
	optCon = poptGetContext(NULL, argc, (const char **)argv, optionsTable, 0);
	poptSetOtherOptionHelp(optCon, "[OPTIONS]*");
	if (argc == 1) {
		about();
	}
	while ((idx = poptGetNextOpt(optCon)) >= 0) {
		switch (idx) {
		case 'i':
			opt |= OPT_INPUT;
			break;
		case 'o':
			opt |= OPT_OUTPUT;
			break;
		case 'a':
			opt |= OPT_ABOUT;
			break;
		case 'v':
			opt |= OPT_VERBOSE;
			break;
		}
	}
	if (idx < -1) {
		fprintf(stderr, "%s: %s\n",
		        poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
		        poptStrerror(idx));
		exit(opt);
	}
	if (opt & OPT_ABOUT) {
		bldinfoPrintBldInfo();
		exit(0);
	}
	TRACE_init((opt & OPT_VERBOSE) ? true : false);

//------------------------------------------------------------------------------
//	Check for MIDI ports
//------------------------------------------------------------------------------
	if (midiInGetNumDevs() == 0) {
		TRACE(INFO,"No Input MIDI ports\n")
		exit(1);
	}
	if (midiOutGetNumDevs() == 0) {
		TRACE(INFO,"No Output MIDI ports\n")
		exit(1);
	}

//------------------------------------------------------------------------------
//	Open MIDI port
//------------------------------------------------------------------------------
	rVal = midiInOpen(&hMIDI_in, MIDI_in, (DWORD)MIDI_proc_in, 0, CALLBACK_FUNCTION);
	if (rVal != MMSYSERR_NOERROR) {
		TRACE(INFO,"midiInOpen() failed. Error code:%d", rVal);
		exit(1);
	}
	rVal = midiOutOpen(&hMIDI_out, MIDI_out, (DWORD)MIDI_proc_out, 0, CALLBACK_FUNCTION);
	if (rVal != MMSYSERR_NOERROR) {
		TRACE(INFO,"midiOutOpen() failed. Error code:%d", rVal);
		exit(1);
	}

//------------------------------------------------------------------------------
//	Prepare buffers for incoming data
//------------------------------------------------------------------------------
	for (int i=0; i<SYSEX_BUFF_CT; i++) {
		SysExHdr[i].lpData = calloc(SYSEX_BUFF_SZ,1);
		SysExHdr[i].dwBufferLength = SYSEX_BUFF_SZ;
		SysExHdr[i].dwBytesRecorded = 0;
		SysExHdr[i].dwUser = i;
		SysExHdr[i].dwFlags = 0;
		midiInPrepareHeader(hMIDI_in, &SysExHdr[i], sizeof(SysExHdr));
		midiInAddBuffer(hMIDI_in, &SysExHdr[i], sizeof(SysExHdr));
	}

//------------------------------------------------------------------------------
//	Wait for q or ESC key
//------------------------------------------------------------------------------
	midiInStart(hMIDI_in);
	while (tr_run == true) {
		in_char = _getch();
		switch (in_char) {
		case 'q':
			tr_run = false;
			break;
		case VK_ESCAPE:
			tr_run = false;
			break;
		}
	}

//------------------------------------------------------------------------------
//	Un-prepare buffers
//	Stop and close the port
//------------------------------------------------------------------------------
	for (int i=0; i<SYSEX_BUFF_CT; i++) {
		midiInUnprepareHeader(hMIDI_in, &SysExHdr[i], sizeof(SysExHdr));
		free(SysExHdr[i].lpData);
	}
	midiInStop(hMIDI_in);
	midiInClose(hMIDI_in);

	return 0;
}

//------------------------------------------------------------------------------
// MIDI input driver callback
//------------------------------------------------------------------------------
static void CALLBACK MIDI_proc_in(HMIDIIN h, UINT wMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
{
	switch(wMsg) {
	case MIM_OPEN:
		TRACE(INFO, "MIM_OPEN");
		break;
	case MIM_CLOSE:
		TRACE(INFO, "MIM_CLOSE");
		break;
	case MIM_DATA:
		TRACE(INFO, "MIM_DATA: MIDI msg:%08x timestamp:%d", dwParam1, dwParam2);
		break;
	case MIM_LONGDATA:
		TRACE(INFO, "MIM_LONGDATA: MIDI hdr:%08x timestamp:%d", dwParam1, dwParam2);
		break;
	case MIM_ERROR:
		TRACE(INFO, "MIM_ERROR");
		break;
	case MIM_LONGERROR:
		TRACE(INFO, "MIM_LONGERROR");
		break;
	case MIM_MOREDATA:
		TRACE(INFO, "MIM_MOREDATA");
		break;
	default:
		TRACE(INFO, "unknown wMsg");
		break;
	}
}

//------------------------------------------------------------------------------
// MIDI output driver callback
//------------------------------------------------------------------------------
static void CALLBACK MIDI_proc_out(HMIDIIN h, UINT wMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
{
	switch(wMsg) {
	case MOM_OPEN:
		TRACE(INFO, "MOM_OPEN");
		break;
	case MOM_CLOSE:
		TRACE(INFO, "MOM_CLOSE");
		break;
	case MOM_DONE:
		TRACE(INFO, "MOM_DONE");
		break;
	default:
		TRACE(INFO, "unknown wMsg");
		break;
	}
}

//------------------------------------------------------------------------------
//	brief
//		Display about
//
//	param
//		none
//
//	return
//		none
//------------------------------------------------------------------------------
static void about(void)
{
	bldinfoPrintBldInfo();
	poptPrintUsage(optCon, stderr, 0);
	exit(1);
}

//
//	End: midi_sysex.c
//
